﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using Pfz.Caching;
using Pfz.Remoting.Internal;

namespace Pfz.Remoting.Instructions
{
	internal sealed class InstructionCollect:
		Instruction
	{
		internal static readonly InstructionCollect Instance = new InstructionCollect();

		private InstructionCollect()
		{
		}

		public override void Run(RemotingClient client, _ThreadData threadData)
		{
			try
			{
				lock(client._wrappersLock)
				{
					try
					{
						client._weCausedActualCollection = true;

						var list = _ClearWrappers(client);

						GC.Collect();
						GC.WaitForPendingFinalizers();
						Thread.Sleep(200);

						List<long> collectedIds = new List<long>();
						var wrappers = new Dictionary<long, _RemotingProxy>(list.Count);
						foreach(var pair in list)
						{
							var id = pair.Key;
							var reference = pair.Value;
							var value = reference.Value;

							reference.Dispose();

							if (value != null)
								wrappers.Add(id, value);
							else
								collectedIds.Add(id);
						}

						client._wrappers = wrappers;

						threadData.Serialize(collectedIds.ToArray());
					}
					finally
					{
						client._weCausedActualCollection = false;
					}
				}
			}
			catch(Exception exception)
			{
				client.Dispose(exception);
			}
		}

		private List<KeyValuePair<long, ThreadUnsafeReference<_RemotingProxy>>> _ClearWrappers(RemotingClient client)
		{
			var wrappers = client._wrappers;
			var result = new List<KeyValuePair<long, ThreadUnsafeReference<_RemotingProxy>>>(wrappers.Count);
			foreach(var pair in wrappers)
			{
				var id = pair.Key;
				var value = pair.Value;
				var reference = new ThreadUnsafeReference<_RemotingProxy>(value, GCHandleType.Weak);
				result.Add(new KeyValuePair<long,ThreadUnsafeReference<_RemotingProxy>>(id, reference));
			}

			wrappers = null;
			client._wrappers = null;

			return result;
		}
	}
}
